<?php
namespace im;

use im\tables\UserLink;
use im\tables\Message as TMessage;
use im\tables\Unread;
use MongoDB\BSON\UTCDateTime;

require_once __DIR__.'/./Message.php';
//table
require_once __DIR__.'/./tables/Message.php';
require_once __DIR__.'/./tables/Unread.php';
require_once __DIR__.'/./tables/UserLink.php';



class MessageAction extends Message{
    
    protected $redis = null;

    public function __construct() {
        
        if(is_null($this->redis)){
            $this->redis  = new \Redis();
            $this->redis->connect("127.0.0.1",123);
            $this->redis->auth('xxx');
        }
        
    }
    
    //@todu: 客户端收到消息后回调方法，减少未读消息总数或置零未读消息数
    
    //socket连接关闭时处理事件
    public function on_close($server, $fd){
        //这里会出现延迟触发情况
        //断线后自动删除在线列表
//        if($this->redis->hExists(self::CHAT_VAL_TO_KEY_MAP,$fd)){
//            $user_map_key = $this->redis->hget(self::CHAT_VAL_TO_KEY_MAP,$fd);
//        echo '删除了'.$user_map_key.'==>'.$fd;
//            $this->redis->hDel(self::CHAT_VAL_TO_KEY_MAP,$fd);
//            $this->redis->hDel(self::CHAT_FD_MAP_KEY,$user_map_key);
//        }
    }
    
    //用户ID 与通道映射
    public function online($params = array(),$server = null,$frame = null){
        $uid = (int)$params['uid'];
        $key = self::CHAT_UID_KEY_PRE.$uid;
        $fd = $frame->fd;
        $this->redis->hSet(self::CHAT_FD_MAP_KEY,$key,$fd); // uid->fd
        $this->redis->hSet(self::CHAT_VAL_TO_KEY_MAP,$fd,$key); // fd->uid
   
        $retrun_msg = [];
        $retrun_msg['action'] = 'online';
        $retrun_msg['data'] = [];
        $retrun_msg['data']['error'] = 0;
        $retrun_msg['data']['msg'] = '登入成功！';
        $server->push($fd, json_encode($retrun_msg));
    }
    
    //发送信息给指定的人 @main
    public function push_msg($params = array(),$server = null,$frame = null){
        $to_uid = (int)$params['to_uid'];
        $from_uid = (int)$params["from_uid"];

         //当天日志记录到缓存
        $this->save_history($params,$server,$frame);
        //未读消息统计增加
        $this->add_unread_record($from_uid, $to_uid);
        //增加历史聊天联系人
        $this->add_user_link($params);
        //推送
        $this->send_msg($params,$server,$frame);
        //通知客户端已经推送成功
        $this->retrun_identifying($params,$server,$frame);
        
    }
    
    //推送消息到客户端
    protected function send_msg($params = array(),$server = null,$frame = null){
        $to_uid = (int)$params['to_uid'];
        $from_uid = (int)$params["from_uid"];
        $msg = $params['msg'];
        $uid_key = self::CHAT_UID_KEY_PRE.$to_uid;
//        var_dump(self::CHAT_FD_MAP_KEY,$uid_key);
        $to_fd = $this->redis->hget(self::CHAT_FD_MAP_KEY,$uid_key);
        
        $return_msg = [];
        $return_msg['action'] = 'get_msg';
        $return_msg['data'] = [];
        $return_msg['data']['error'] = 0;
        $return_msg['data']['from_uid'] = $from_uid;
        $return_msg['data']['to_uid'] = $to_uid;
        $return_msg['data']['msg'] = $msg;
        $return_msg['data']['time'] = time();
        
        $push_result = false;
        //检查发送准确度
        $check_online = $this->check_receive($uid_key,$to_fd,$server);

        if(!$server->exist($to_fd)){
//            var_dump($server->exist($to_fd));
            $server->close($to_fd);
        }else{
            if($check_online){
                var_dump($to_fd);
                   $push_result = $server->push($to_fd, json_encode($return_msg));
                var_dump($push_result);
            }
        }
    }
    
    //增加未读消息记录
    protected function add_unread_record($from_uid,$to_uid){
        //增加子未读
        $uid = $to_uid;
        $unread_key = self::CHAT_UNREAD_MSG_COUNT.$uid;// 接收者
        $f_user_key = self::CHAT_UNREAD_USER_KEY.$from_uid; //发送者
        $unread_count_num = 1;
        //设置来自谁的消息未读数量
        if($this->redis->hExists($unread_key,$f_user_key)){
            $unread_count_num = $this->redis->hGet($unread_key,$f_user_key);
            $unread_count_num+=1;
        }
        
        $this->redis->hSet($unread_key,$f_user_key,$unread_count_num);
        
        //设置总数
        $unread_count_num = 1;
        $unread_sum_key = self::CHAT_UNREAD_USER_KEY.$uid; //接收者总数KEY
        if($this->redis->hExists(self::CHAT_UNREAD_MSG_SUM,$unread_sum_key)){
            $unread_count_num = $this->redis->hGet(self::CHAT_UNREAD_MSG_SUM,$unread_sum_key);
            $unread_count_num+=1;
        }
        
        $this->redis->hSet(self::CHAT_UNREAD_MSG_SUM,$unread_sum_key,$unread_count_num);
        //保存到数据库
//        $this->save_unread_to_mongo($from_uid, $to_uid);
    }
    
    //保存未读消息到数据库
    protected function save_unread_to_mongo($from_uid,$to_uid){
        $collect = self::getCollect('user_unread_num');
        $result = $collect->findOne(["uid"=>$to_uid]);
        
        if($result){
            $num = $result->num+1;
            $unread_map = $result->unread_map;
            $unread_map_num = ( isset($unread_map[$from_uid]) ? ($unread_map[$from_uid]+1) : 0); 
            $unread_map[$from_uid] = $unread_map_num;
            $update_result = $collect->updateOne(["uid"=>$to_uid],
                    ['$set'=>[ 'num'=>$num,'unread_map'=>$unread_map ]]
                    );
        }else{
            $unread_obj = new tables\Unread($from_uid, $to_uid);
            $collect->insertOne($unread_obj);
        }
    }
    
    //增加联系人
    protected function add_user_link($params = array()){
        //接收者映射发送者
        $to_uid = (int)$params['to_uid'];
        $from_uid = (int)$params['from_uid'];
        $msg = $params['msg'];
        $time = time();
        $info = json_encode(['time'=>$time,'msg'=>$msg]);

        $user_msg_links_key_receive = self::CHAT_USER_LINKS.$to_uid;
        $from_user_link_key = self::CHAT_UNREAD_USER_KEY.$from_uid;

        $this->redis->hSet($user_msg_links_key_receive,$from_user_link_key,$info);
        //发送者映射接收者
        $to_user_link_key = self::CHAT_UNREAD_USER_KEY.$to_uid;
        $user_msg_links_key_send = self::CHAT_USER_LINKS.$from_uid;

        $this->redis->hSet($user_msg_links_key_send,$to_user_link_key,$info);
        //=== 持久化：保存到数据库
        $this->save_user_link($from_uid, $to_uid,$info);
    }
    
    //保存联系人列表数据到数据库
    protected function save_user_link($from_uid,$to_uid,$info){
        // uid: to_uid,map: [ from_uid: time ]
        $collect = self::getCollect('user_links_map');
        $from_result = $collect->findOne(["uid"=>$from_uid]);
        $to_result = $collect->findOne(["uid"=>$to_uid]);
        
        //发送者
        if($from_result){
            
            if(isset($from_result->link_map[$to_uid])){
                $from_result->link_map[$to_uid] = (string)($info);
                $collect->updateOne(["uid"=>$from_uid],
                    ['$set'=>[ 'link_map'=>$from_result->link_map ]]
                );
            }

        }else{
            $user_link_map = new tables\UserLink($from_uid, $to_uid,$info);
            $collect->insertOne($user_link_map);
        }
        
        //接收者
        if($to_result){
            
            if(isset($to_result->link_map[$from_uid])){
                $to_result->link_map[$from_uid] = (string)($info);
                $collect->updateOne(["uid"=>$to_uid],
                    ['$set'=>[ 'link_map'=>$to_result->link_map ]]
                );
            }

        }else{
            $user_link_map = new tables\UserLink($to_uid, $from_uid,$info);
            $collect->insertOne($user_link_map);
        }
        
    }
    
    //判断当前连接此FD的人是不是我要发送的人
    protected function check_receive($to_uid_key = '',$to_fd = 0,$server = null){
        
        //检查当前FD对应的UID_KEY和要发送的UID_KEY是否一致,如果不一致，则删除UID->FD映射关系[表示此人不在线]，保留FD->UID_KEY映射[保留最新的FD映射关系]
        if($this->redis->hExists(self::CHAT_VAL_TO_KEY_MAP,$to_fd)){
            $fd_uid_key = $this->redis->hget(self::CHAT_VAL_TO_KEY_MAP,$to_fd);
            
            if($fd_uid_key != $to_uid_key){
                $this->redis->hDel(self::CHAT_FD_MAP_KEY,$to_uid_key);
                return false;
            }
        }else{
            return false;
        }
        
        return true;
    }

    //将当天的聊天记录保存在缓存中，每天晚12点清理，即时存入数据库
    protected function save_history($params = array()){
        //缓存里发送人的记录一次，接收人的记录一次,读取记录省事
        $to_uid = (int)$params['to_uid'];
        $from_uid = (int)$params["from_uid"];
        $msg = $params['msg'];
        $time = date("Y-m-d H:i:s");
        $end_time = mktime(0,0,0,date('m',time()),date("d",strtotime('+1 day')),date('Y',time())) - time();
        $sender_key = self::CHAT_USER_HISTORY_KEY.$from_uid;
        $sender_msg_key = self::CHAT_HISTORY_USER_KEY.$to_uid;
        $receiver_key = self::CHAT_USER_HISTORY_KEY.$to_uid;
        $recevier_msg_key = self::CHAT_HISTORY_USER_KEY.$from_uid;
        //============== 发送者记录
        if( $this->redis->hExists($sender_key,$sender_msg_key) ){
            
            $msgs = $this->redis->hget($sender_key,$sender_msg_key);
            $msg_raw = json_decode($msgs,true);
            $msg_count = count($msg_raw);
            $next_subscript = $msg_count+1;
            
        }else{
            $msg_raw = [];
            $next_subscript = 0;
        }
        
        $msg_raw[$next_subscript] = [];
        $msg_raw[$next_subscript]['from_uid'] = $from_uid;
        $msg_raw[$next_subscript]['to_uid'] = $to_uid;
        $msg_raw[$next_subscript]['msg'] = $msg;
        $msg_raw[$next_subscript]['time'] = $time;
        $msg_string = json_encode($msg_raw);
        $this->redis->hset($sender_key,$sender_msg_key,$msg_string);
        $this->redis->expire($sender_key,$end_time);
        //================== 接收者记录
        if( $this->redis->hExists($receiver_key,$recevier_msg_key) ){

            $msgs = $this->redis->hget($receiver_key,$recevier_msg_key);
            $msg_raw = json_decode($msgs,true);
            $msg_count = count($msg_raw);
            $next_subscript = $msg_count+1;

        }else{
            $msg_raw = [];
            $next_subscript = 0;
        }
        
        $msg_raw[$next_subscript] = [];
        //冗余字段
        $msg_raw[$next_subscript]['from_uid'] = $from_uid;
        $msg_raw[$next_subscript]['to_uid'] = $to_uid;
        $msg_raw[$next_subscript]['msg'] = $msg;
        $msg_raw[$next_subscript]['time'] = $time;
        $msg_string = json_encode($msg_raw);
        $this->redis->hset($receiver_key,$recevier_msg_key,$msg_string);
        $this->redis->expire($receiver_key,$end_time);
        //================= 保存到数据库,to MongoDB
        
        $msg_obj = new tables\Message($from_uid, $to_uid, $msg);
        $result = self::store('message', $msg_obj);

    }
    
    //发送消息后返回确认信息给客户端
    protected function retrun_identifying($params = array(),$server = null,$frame = null){
        $to_uid = (int)$params['to_uid'];
        $from_uid = (int)$params["from_uid"];
        $msg = $params['msg'];
        $fd = $frame->fd;
        
        $return_msg = [];
        $return_msg['action'] = 'show_send_msg';
        $return_msg['data'] = [];
        $return_msg['data']['error'] = 0;
        $return_msg['data']['from_uid'] = $from_uid;
        $return_msg['data']['to_uid'] = $to_uid;
        $return_msg['data']['msg'] = $msg;
        $return_msg['data']['time'] = time();
        
        $server->push($fd, json_encode($return_msg));
    }

    //标记已读消息
    public function read_msg($params = array(),$server = null,$frame = null){
        $from_uid = (int)$params['from_uid']; //谁发给我的
        $to_uid = (int)$params['to_uid']; //接收者

        $read_time_timestample = (time()+(8*60*60))*1000;
        $read_time = new UTCDateTime($read_time_timestample);

        self::getCollect('message')->updateMany(
            [
                'from_uid'=>$from_uid,
                'to_uid'=>$to_uid,
                'read_time'=>0
            ],
            [ '$set' => ['read_time'=>$read_time] ]
        );
        //减少未读消息数量
        $unread_key = Message::CHAT_UNREAD_MSG_COUNT.$from_uid;
        $f_user_key = Message::CHAT_UNREAD_USER_KEY.$to_uid;
        //清空所有此人发的未读消息计数
        $user_unread_count_num = 0;
        if($this->redis->hExists($unread_key,$f_user_key)){
            $user_unread_count_num = $this->redis->hGet($unread_key,$f_user_key);
        }
        $this->redis->hSet($unread_key,$f_user_key,0);

        //设置总数减少
        $unread_count_num = 0;
        $unread_sum_key = Message::CHAT_UNREAD_USER_KEY.$from_uid; //接收者总数KEY
        if($this->redis->hExists(Message::CHAT_UNREAD_MSG_SUM,$unread_sum_key)){
            $unread_count_num = $this->redis->hGet(Message::CHAT_UNREAD_MSG_SUM,$unread_sum_key);
            $unread_count_num = ( $unread_count_num >= $user_unread_count_num) ? ($unread_count_num - $user_unread_count_num) : ($unread_count_num);
        }

        $this->redis->hSet(Message::CHAT_UNREAD_MSG_SUM,$unread_sum_key,$unread_count_num);
    }
}

